////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//                                                                                                                            //
//                                                         WNProject                                                          //
//                                                                                                                            //
//            This file is distributed under the BSD 2-Clause open source license. See License.txt for details.               //
//                                                                                                                            //
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

#pragma once

#ifndef __WN_CONTAINERS_CALLBACK_H__
#define __WN_CONTAINERS_CALLBACK_H__

#include "WNCore/inc/WNTypes.h"
#include "WNMemory/inc/WNAllocation.h"

namespace WNContainers {
    template <typename Return, typename Parameter>
    class __WNCallbackBase1 {
    public:
        __WNCallbackBase1() { }
        virtual ~__WNCallbackBase1(); 
        virtual Return Execute(Parameter _parameter) const = 0;

        WN_VOID AddRef();
        WN_ATOM_T RemoveRef();

    private:
        WN_ATOM_T mParameter;
    };

    template <typename Type, typename Return, typename Parameter>
    class __WNCallbackInternal1: public __WNCallbackBase1<Return, Parameter> {
    public:
        __WNCallbackInternal1(Return (Type::*_function)(Parameter), Type* _this);

        virtual Return Execute(Parameter _parameter) const;

    private:
        Type* mThis;
        Return (Type::*mFunction)(Parameter);
    };

    template <typename Return, typename Parameter>
    class WNCallback1 {
    public:
        WNCallback1();
        WNCallback1(const WNCallback1& _callback);
        WNCallback1(Return (*_function)(Parameter));

        // Note that due to a bug in MSVC 10 and lower this cannot be put into the inline file
        template <typename Type, typename R, typename P>
        WNCallback1(Type* _this, R (Type::*_function(P))) {
            mBaseCallback = new __WNCallbackInternal1<Type, R, P>(_this, _function);

            if (mBaseCallback) {
                mBaseCallback->AddRef();
            }
        }

        virtual ~WNCallback1();

        WNCallback1<Return, Parameter>& operator = (const WNCallback1& _callback);

        Return Execute(Parameter _parameter) const;

    private:
        __WNCallbackBase1<Return, Parameter>* mBaseCallback;
    };

    template<typename Return, typename Parameter1, typename Parameter2>
    class __WNCallbackBase2 {
    public:
        __WNCallbackBase2();
        virtual ~__WNCallbackBase2() { }
        virtual Return Execute(Parameter1 _param, Parameter2 _param2) const = 0;

        WN_VOID AddRef();
        WN_ATOM_T RemoveRef();

    private:
        WN_ATOM_T mParameter;
    };

    template <typename Type, typename Return, typename Parameter1, typename Parameter2>
    class __WNCallbackInternal2: public __WNCallbackBase2<Return, Parameter1, Parameter2> {
    public:
        __WNCallbackInternal2(Return (Type::*_function)(Parameter1, Parameter2), Type* _this);

        virtual Return Execute(Parameter1 _param, Parameter2 _param2) const;

    private:
        Type* mThis;
        Return (Type::*mFunction)(Parameter1, Parameter2);
    };

    template <typename Return, typename Parameter1, typename Parameter2>
    class WNCallback2 {
    public:
        WNCallback2();
        WNCallback2(const WNCallback2& _callback);
        WNCallback2(Return (*_function)(Parameter1, Parameter2));

        template<typename Type, typename R, typename P1, typename P2>
        WNCallback2(Type* _this, R (Type::*_function(P1, P2))) {
            mBaseCallback = new __WNCallbackInternal2<Type, R, P1, P2>(_this, _function);

            if (mBaseCallback) {
                mBaseCallback->AddRef();
            }
        }

        virtual ~WNCallback2();

        WNCallback2<Return, Parameter1, Parameter2>& operator = (const WNCallback2& _callback);

        Return Execute(Parameter1 _param1, Parameter2 _param2) const;

    private:
        __WNCallbackBase2<Return, Parameter1, Parameter2>* mBaseCallback;
    };
}

#include "WNContainers/inc/WNCallback.inl"

#endif // __WN_CONTAINERS_CALLBACK_H__
